import binascii
import json

import serial


class DLT645Result:
    all_energy: float  # 总电能
    all_power: float  # 瞬时总有功功率
    A_voltage: float  # A电压
    A_current: float  # A电流
    A_angle: float  # A相角
    zero_line_current: float  # 零线电流
    grid_frequency: float  # 电网频率

    def to_json(self):
        return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4)


class DLT645F2007Protocol:

    def __init__(self, logger, addr: str, ser_port):
        self.ser_port = ser_port
        self.logger = logger
        self.addr = addr
        addr_list = [addr[i:i + 2] for i in range(len(addr) - 2, -1, -2)]
        addr_list.reverse()
        self.addr_list = addr_list
        self.protocol_switch = {
            "2007": ["69", "00", "16"],
            "1997": ["69", "01", "16"]
        }
        # A 相电流
        self.A_current_cmd = self.gen_cmd(addr_list, "11", "04", ["33", "34", "35", "35"])
        # 总电能
        self.all_energy_cmd = self.gen_cmd(addr_list, "11", "04", ["33", "33", "34", "33"])
        # 瞬时总有功功率
        self.all_power_cmd = self.gen_cmd(addr_list, "11", "04", ["33", "33", "36", "35"])
        # A 相电压
        self.A_voltage_cmd = self.gen_cmd(addr_list, "11", "04", ["33", "34", "34", "35"])
        # A 相相角
        self.A_angle_cmd = self.gen_cmd(addr_list, "11", "04", ["33", "36", "3A", "35"])
        # 零线电流
        self.zero_line_current_cmd = self.gen_cmd(addr_list, "11", "04", ["34", "32", "B3", "35"])
        # 电网频率
        self.grid_frequency_cmd = self.gen_cmd(addr_list, "11", "04", ["35", "33", "B3", "35"])

    def gen_cmd(self, addr: list[str], control: str, data_len: str, data: list[str]):
        prefix = ["FE", "FE", "FE", "FE", "68", ]
        end = ["16"]
        verify_code = ["00"]
        ret = prefix + addr + ["68"] + [control] + [data_len] + data + verify_code + end
        apv = self.get_verify_code(ret)
        ret[-2] = apv
        return ret

    def get_verify_code(self, data):
        # 第一个起始码68至校验码前一个字节的和取低8位。
        verify_code = 0
        data = data[4:-2]
        for val_str in data:
            verify_code += int(val_str, 16)
        verify_code = verify_code % 256
        return hex(verify_code).upper()[2:].zfill(2)

    def calc_sum(self, al):
        verify_sum = 0
        for val_str in al:
            verify_sum += int(val_str, 16)
        return hex(verify_sum % 256).upper()[2:].zfill(2)

    def struct_cmd(self, address, data):
        command = data[:]
        command[5] = address[10:12]
        command[6] = address[8:10]
        command[7] = address[6:8]
        command[8] = address[4:6]
        command[9] = address[2:4]
        command[10] = address[0:2]
        checkSum = self.calc_sum(command[4:-2])
        command[-2] = checkSum
        self.logger.info(f"send_data{command}")
        return command

    def to_list_data(self, data):
        resData = []
        count = len(data)
        i = 0
        j = 0
        while i < count:
            resData.append(data[i: i + 2].decode("utf-8"))
            j = j + 1
            i = i + 2
        self.logger.info(f"toListData:{resData}")

        return resData

    def check_data(self, data):
        resData = self.to_list_data(data)
        self.logger.info(resData)
        checkLen = str(self.calc_sum(resData[0:-2]))
        _len = "".join(resData[-2:-1]).upper()
        if checkLen == _len:
            self.logger.info("CScheck success")
            dataLen = int("".join(resData[9:10]), 16)
            if "".join(resData[10 + dataLen: 11 + dataLen]).upper() == _len:
                self.logger.info("dataLen success")
                return dataLen
            else:
                self.logger.info("dataLen fails")
                return 0
        else:
            self.logger.info("CScheck fails")
            return 0

    def analysis_data(self, data):
        resData = self.to_list_data(data)
        dataLen = self.check_data(data)
        if dataLen < 1:
            self.logger.error("read data fail")
            return -1
        i = 1
        while i <= dataLen:
            dataA = int("".join(resData[9 + i]), 16) - int("33", 16)
            dataB = hex(dataA).upper()[2:].zfill(2)
            resData[9 + i] = dataB
            i = i + 1
        dataList_b = resData[10: 10 + dataLen]
        dataList = resData[10: 10 + dataLen]
        for i in dataList_b:
            dataList[dataLen - 1] = i
            dataLen = dataLen - 1
        return int("".join(dataList[0:-4]))

    def send_data(self, data, ser):
        cmd = self.struct_cmd(self.addr, data)
        command = binascii.unhexlify("".join(cmd))
        ser.write(command)
        response = ser.readall()
        ser.flushInput()
        return response

    def send_cmd(self) -> DLT645Result | None:
        data_result = DLT645Result()

        ser = serial.Serial(
            self.ser_port,
            2400,
            timeout=1,
            parity=serial.PARITY_EVEN,
            bytesize=serial.EIGHTBITS,
        )

        self.logger.info(f"serial.isOpen()={ser.isOpen()}")
        if ser.is_open:
            response = self.send_data(self.all_energy_cmd, ser)
            all_energy_data = self.analysis_data(binascii.hexlify(response)) / 100.00
            if all_energy_data < 0:
                return None
            self.logger.info(f"all_energyData:{all_energy_data}")
            data_result.all_energy = all_energy_data

            response = self.send_data(self.all_power_cmd, ser)
            all_power_data = self.analysis_data(binascii.hexlify(response)) / 10000.0000
            if all_power_data < 0:
                return None
            self.logger.info(f"all_powerData:{all_power_data}")
            data_result.all_power = all_power_data

            response = self.send_data(self.A_voltage_cmd, ser)
            A_voltage = self.analysis_data(binascii.hexlify(response)) / 10.0
            if A_voltage < 0:
                return None
            self.logger.info(f"A_voltage:{A_voltage}")
            data_result.A_voltage = A_voltage

            response = self.send_data(self.A_current_cmd, ser)
            A_current = self.analysis_data(binascii.hexlify(response)) / 1000.000
            if A_current < 0:
                return None
            self.logger.info(f"A_current:{A_current}")
            data_result.A_current = A_current

            # response = self.send_data(self.A_angle_cmd, ser)
            # A_angle = self.analysis_data(binascii.hexlify(response))
            # if A_angle < 0:
            #     return None
            # self.logger.info(f"A_angle:{A_angle}")
            # data_result.A_angle = A_angle
            #
            # response = self.send_data(self.zero_line_current_cmd, ser)
            # zero_line_current = self.analysis_data(binascii.hexlify(response))
            # if zero_line_current < 0:
            #     return None
            # self.logger.info(f"zero_line_current:{zero_line_current}")
            # data_result.zero_line_current = zero_line_current
            #
            # response = self.send_data(self.grid_frequency_cmd, ser)
            # grid_frequency = self.analysis_data(binascii.hexlify(response))
            # if grid_frequency < 0:
            #     return None
            # self.logger.info(f"grid_frequency:{grid_frequency}")
            # data_result.grid_frequency = grid_frequency

            ser.close()
            self.logger.info(f"serial.isOpen()={ser.isOpen()}")
            return data_result
        else:
            return None
